package com.xiyuan.smartutils;

import com.xiyuan.smartutils.constants.CodeConstants;
import com.xiyuan.smartutils.constants.TypeConstants;
import com.xiyuan.smartutils.filters.Filter;

import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;

/**
 * <bre>
 * 字符串扩展工具类
 * 1.继承Stringx类，实现基础的字符串工具功能
 * 2.创建Strings类，实现扩展的字符串工具功能
 * </bre>
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */

@SuppressWarnings("all")
public final class StrUtils implements CodeConstants, TypeConstants {
    
    public static final int INDEX_NOT_FOUND = -1;
    public static final char C_BACKSLASH = _BACK_SLASH_;
    public static final char C_SLASH = _SLASH_;
    public static final char C_SPACE = _SPACE_;
    public static final char C_DOT = _DOT_;
    public static final char C_UNDERLINE = _UNDERLINE_;
    public static final String EMPTY_JSON = "{}";
    public static final String DOT = ".";
    public static final String COLON = ":";
    
    
    /**
     * 字符串常量：双点 {@code ".."} <br>
     * 用途：作为指向上级文件夹的路径，如：{@code "../path"}
     */
    public static final String DOUBLE_DOT = "..";
    
    /**
     * 字符串常量：斜杠 {@code "/"}
     */
    public static final String SLASH = "/";
    
    /**
     * 字符串常量：反斜杠 {@code "\\"}
     */
    public static final String BACKSLASH = "\\";
    public static final String NULL = "null";
    /**
     * 字符串常量：空字符串 {@code ""}
     */
    public static final String EMPTY = "";
    
    private StrUtils() {}
    
    public static int length(CharSequence s) {
        return (s == null) ? 0 : s.length();
    }
    
    public static boolean isEmpty(CharSequence s) {
        return Validates.isEmpty(s);
    }
    
    public static boolean isNotEmpty(CharSequence s) {
        return Validates.isNotEmpty(s);
    }
    
    public static boolean isBlank(CharSequence s) {return Validates.isBlank(s);}
    
    public static boolean isNotBlank(CharSequence s) {return Validates.isNotBlank(s);}
    
    public static boolean isWhitespace(char c) {return Validates.isWhitespace(c);}
    
    /**
     * <p>是否包含空字符串。</p>
     * <p>如果指定的字符串数组的长度为 0，或者其中的任意一个元素是空字符串，则返回 true。</p>
     * <br>
     *
     * <p>例：</p>
     * <ul>
     *     <li>{@code StrUtil.hasEmpty()                  // true}</li>
     *     <li>{@code StrUtil.hasEmpty("", null)          // true}</li>
     *     <li>{@code StrUtil.hasEmpty("123", "")         // true}</li>
     *     <li>{@code StrUtil.hasEmpty("123", "abc")      // false}</li>
     *     <li>{@code StrUtil.hasEmpty(" ", "\t", "\n")   // false}</li>
     * </ul>
     *
     * @param strs 字符串列表
     * @return 是否包含空字符串
     */
    public static boolean hasEmpty(CharSequence... strs) {
        if (Arrays.isEmpty(strs)) {
            return true;
        }
        
        for (CharSequence str : strs) {
            if (isEmpty(str)) {
                return true;
            }
        }
        return false;
    }
    
    public static char charAt(CharSequence s, int index) {
        return s.charAt(index);
    }
    
    public static int codePointAt(String s, int index) {
        return s.codePointAt(index);
    }
    
    public static int codePointBefore(String s, int index) {
        return s.codePointBefore(index);
    }
    
    public static int codePointCount(String s, int beginIndex, int endIndex) {
        return s.codePointCount(beginIndex, endIndex);
    }
    
    public static int offsetByCodePoints(String s, int index, int codePointOffset) {
        return s.offsetByCodePoints(index, codePointOffset);
    }
    
    public static void getChars(String s, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
        s.getChars(srcBegin, srcEnd, dst, dstBegin);
    }
    
    public static byte[] getBytesUTF8(String s) {
        return s.getBytes(_UTF_8_C_);
    }
    
    public static String newString(byte[] bytes, String encoding) {
        return new String(bytes, Charset.forName(encoding));
    }
    
    public static String newStringUTF8(byte[] bytes) {
        return newString(bytes, _UTF_8_);
    }
    
    public static boolean equals(String s, String a) {
        
        return Objects.equals(s, a);
    }
    
    public static boolean equals(char a, char b, boolean ignoreCase) {
        return a == b || ignoreCase && Character.toLowerCase(a) == Character.toLowerCase(b);
    }
    
    public static boolean contentEquals(String s, StringBuffer sb) {
        return (s == null) ? (sb == null) : s.contentEquals(sb);
    }
    
    public static boolean contentEquals(String s, CharSequence cs) {
        return (s == null) ? (cs == null) : s.contentEquals(cs);
    }
    
    public static int compareTo(String s, String a) {
        return s.compareTo(a);
    }
    
    public static int compareToIgnoreCase(String s, String a) {
        return s.compareToIgnoreCase(a);
    }
    
    public static boolean regionMatches(String s, int toffset, String other, int ooffset, int len) {
        return (s == null) ? (other == null) : s.regionMatches(toffset, other, ooffset, len);
    }
    
    public static boolean regionMatches(String s, boolean ignoreCase, int toffset, String other, int ooffset, int len) {
        return (s == null) ? (other == null) : s.regionMatches(ignoreCase, toffset, other, ooffset, len);
    }
    
    /**
     * 字符串是否以给定字符开始
     *
     * @param str 字符串
     * @param c   字符
     * @return 是否开始
     */
    public static boolean startsWith(CharSequence str, char c) {
        if (isEmpty(str)) return false;
        return c == str.charAt(0);
    }
    
    public static boolean startsWith(String s, String prefix, int toffset) {
        return (s == null) ? (prefix == null) : s.startsWith(prefix, toffset);
    }
    
    public static boolean startsWith(String s, String prefix) {
       return startsWith(s, prefix, false);
    }
    
    public static boolean startsWith(CharSequence s, CharSequence prefix, boolean ignoreCase) {
        if (s == null) {
            return prefix == null;
        }
        
        if (prefix == null || prefix.length() > s.length()) {
            return false;
        }
        
        int i = 0, l = prefix.length();
        while (i < l && equals(prefix.charAt(i), s.charAt(i), ignoreCase)) {i++;}
        return i == l;
    }
    
    public static boolean endsWith(CharSequence s, CharSequence suffix) {
        return endsWith(s, suffix, false);
    }
    
    public static boolean endsWith(CharSequence s, CharSequence suffix, boolean ignoreCase) {
        if (s == null) {
            return suffix == null;
        }
        
        if (suffix == null || suffix.length() > s.length()) {
            return false;
        }
        
        int i = 0, l = suffix.length(), offset = s.length() - suffix.length();
        while (i < l && equals(suffix.charAt(i), s.charAt(offset + i), ignoreCase)) {i++;}
        return i == l;
    }
    
    public static int hashCode(String s) {
        return (s == null) ? 0 : s.hashCode();
    }
    
    public static int indexOf(String s, int ch) {
        return (s == null) ? -1 : s.indexOf(ch);
    }
    
    public static int indexOf(String s, int ch, int fromIndex) {
        return (s == null) ? -1 : s.indexOf(ch, fromIndex);
    }
    
    public static int lastIndexOf(String s, int ch) {
        return (s == null) ? -1 : s.lastIndexOf(ch);
    }
    
    public static int lastIndexOf(String s, int ch, int fromIndex) {
        return (s == null) ? -1 : s.lastIndexOf(ch, fromIndex);
    }
    
    public static int indexOf(String s, String str) {
        return (s == null) ? -1 : s.indexOf(str);
    }
    
    public static int indexOf(String s, String str, int fromIndex) {
        return (s == null) ? -1 : s.indexOf(str, fromIndex);
    }
    
    public static int lastIndexOf(String s, String str) {
        return (s == null) ? -1 : s.lastIndexOf(str);
    }
    
    public static int lastIndexOf(String s, String str, int fromIndex) {
        return (s == null) ? -1 : s.lastIndexOf(str, fromIndex);
    }
    
    public static String substring(String s, int beginIndex) {
        return s.substring(beginIndex);
    }
    
    public static String substring(String s, int beginIndex, int endIndex) {
        return s.substring(beginIndex, endIndex);
    }
    
    public static CharSequence subSequence(String s, int beginIndex, int endIndex) {
        return s.subSequence(beginIndex, endIndex);
    }
    
    public static String concat(String s, String str) {
        return s.concat(str);
    }
    
    public static String replace(String s, char oldChar, char newChar) {
        return s == null ? s : s.replace(oldChar, newChar);
    }
    
    public static boolean matches(String s, String regex) {
        return (s == null) ? (regex == null) : s.matches(regex);
    }
    
    
    public static boolean contains(String s, CharSequence cs) {
        return (s == null) ? (cs == null) : s.contains(cs);
    }
    
    public static boolean containsIgnoreCase(CharSequence s, String substring) {
        return (s == null) ? (substring == null) : s.toString().contains(substring.toLowerCase());
    }
    
    public static boolean contains(CharSequence s1, CharSequence s2) {
        if(s1 == s2) return true;
        if (s1 == null || s2 == null) return false;
        int len1 = s1.length();
        int len2 = s2.length();
        if(len1 < len2) return false;
        return s1.toString().contains(s2);
    }
    
    public static String replaceFirst(String s, String regex, String replacement) {
        return s.replaceFirst(regex, replacement);
    }
    
    public static String replaceAll(String s, String regex, String replacement) {
        return s.replaceAll(regex, replacement);
    }
    
    public static String replace(String s, CharSequence target, CharSequence replacement) {
        return s.replace(target, replacement);
    }
    
    /**
     * 将对象转为字符串<br>
     *
     * <pre>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
     * 2、对象数组会调用Arrays.toString方法
     * </pre>
     *
     * @param obj 对象
     * @return 字符串
     */
    public static String utf8Str(Object obj) {
        return str(obj, _UTF_8_C_);
    }
    
    /**
     * {@link CharSequence} 转为字符串，null安全
     *
     * @param cs {@link CharSequence}
     * @return 字符串
     */
    public static String str(CharSequence cs) {
        return null == cs ? null : cs.toString();
    }
    
    /**
     * 将对象转为字符串
     *
     * <pre>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
     * 2、对象数组会调用Arrays.toString方法
     * </pre>
     *
     * @param obj         对象
     * @param charsetName 字符集
     * @return 字符串
     * @deprecated 请使用 {@link #str(Object, Charset)}
     */
    @Deprecated
    public static String str(Object obj, String charsetName) {
        return str(obj, Charset.forName(charsetName));
    }
    
    /**
     * 将对象转为字符串
     * <pre>
     * 	 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
     * 	 2、对象数组会调用Arrays.toString方法
     * </pre>
     *
     * @param obj     对象
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(Object obj, Charset charset) {
        if (null == obj) {
            return null;
        }
        
        if (obj instanceof String) {
            return (String) obj;
        }
        else if (obj instanceof byte[]) {
            return str((byte[]) obj, charset);
        }
        else if (obj instanceof Byte[]) {
            return str((Byte[]) obj, charset);
        }
        else if (obj instanceof ByteBuffer) {
            return str((ByteBuffer) obj, charset);
        }
        else if (obj != null && obj.getClass().isArray()) {
            return Arrays.toString(obj);
        }
        
        return obj.toString();
    }
    
    /**
     * 将byte数组转为字符串
     *
     * @param bytes   byte数组
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(byte[] bytes, String charset) {
        return str(bytes, Charset.forName(charset));
    }
    
    /**
     * 解码字节码
     *
     * @param data    字符串
     * @param charset 字符集，如果此字段为空，则解码的结果取决于平台
     * @return 解码后的字符串
     */
    public static String str(byte[] data, Charset charset) {
        if (data == null) {
            return null;
        }
        
        if (null == charset) {
            return new String(data);
        }
        return new String(data, charset);
    }
    
    /**
     * 将Byte数组转为字符串
     *
     * @param bytes   byte数组
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(Byte[] bytes, String charset) {
        return str(bytes, Charset.forName(charset));
    }
    
    /**
     * 解码字节码
     *
     * @param data    字符串
     * @param charset 字符集，如果此字段为空，则解码的结果取决于平台
     * @return 解码后的字符串
     */
    public static String str(Byte[] data, Charset charset) {
        if (data == null) {
            return null;
        }
        
        byte[] bytes = new byte[data.length];
        Byte dataByte;
        for (int i = 0; i < data.length; i++) {
            dataByte = data[i];
            bytes[i] = (null == dataByte) ? -1 : dataByte;
        }
        
        return str(bytes, charset);
    }
    
    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data    数据
     * @param charset 字符集，如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, String charset) {
        if (data == null) {
            return null;
        }
        
        return str(data, Charset.forName(charset));
    }
    
    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data    数据
     * @param charset 字符集，如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, Charset charset) {
        if (null == charset) {
            charset = Charset.defaultCharset();
        }
        return charset.decode(data).toString();
    }
    
    /**
     * 替换指定字符串的指定区间内字符为固定字符<br>
     * 此方法使用{@link String#codePoints()}完成拆分替换
     *
     * @param str          字符串
     * @param startInclude 开始位置（包含）
     * @param endExclude   结束位置（不包含）
     * @param replacedChar 被替换的字符
     * @return 替换后的字符串
     *
     */
    public static String replace(String str, int startInclude, int endExclude, char replacedChar) {
        if (isEmpty(str)) {
            return str;
        }
        
        int[] strCodePoints = str.codePoints().toArray();
        final int strLength = strCodePoints.length;
        if (startInclude > strLength) {
            return str;
        }
        if (endExclude > strLength) {
            endExclude = strLength;
        }
        if (startInclude > endExclude) {
            // 如果起始位置大于结束位置，不替换
            return str;
        }
        
        final StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < strLength; i++) {
            if (i >= startInclude && i < endExclude) {
                stringBuilder.append(replacedChar);
            }
            else {
                stringBuilder.append(new String(strCodePoints, i, 1));
            }
        }
        return stringBuilder.toString();
    }
    
    /**
     * 替换指定字符串的指定区间内字符为"*"
     * 俗称：脱敏功能，
     *
     * <pre>
     * StrUtil.hide(null,*,*)=null
     * StrUtil.hide("",0,*)=""
     * StrUtil.hide("jackduan@163.com",-1,4)   ****duan@163.com
     * StrUtil.hide("jackduan@163.com",2,3)    ja*kduan@163.com
     * StrUtil.hide("jackduan@163.com",3,2)    jackduan@163.com
     * StrUtil.hide("jackduan@163.com",16,16)  jackduan@163.com
     * StrUtil.hide("jackduan@163.com",16,17)  jackduan@163.com
     * </pre>
     *
     * @param str          字符串
     * @param startInclude 开始位置（包含）
     * @param endExclude   结束位置（不包含）
     * @return 替换后的字符串
     *
     */
    public static String hide(String str, int startInclude, int endExclude) {
        return replace(str, startInclude, endExclude, '*');
    }
    
    /**
     * 过滤字符串
     *
     * @param str    字符串
     * @param filter 过滤器，{@link Filter#accept(Object)}返回为{@code true}的保留字符
     * @return 过滤后的字符串
     *
     */
    public static String filter(CharSequence str, final Filter<Character> filter) {
        if (str == null || filter == null) {
            return str == null ? null : str.toString();
        }
        
        int len = str.length();
        final StringBuilder sb = new StringBuilder(len);
        char c;
        for (int i = 0; i < len; i++) {
            c = str.charAt(i);
            if (filter.accept(c)) {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    
    /**
     * 清理空白字符
     *
     * @param str 被清理的字符串
     * @return 清理后的字符串
     */
    public static String cleanBlank(CharSequence str) {
        return filter(str, c -> !Validates.isWhitespace(c));
    }
    
    public static String[] split(String s, String regex, int limit) {
        return s.split(regex, limit);
    }
    
    public static String[] split(String s, String regex) {
        return s.split(regex);
    }
    
    /**
     * 通配符拆分
     * 原始字符串： com.xiyuan.ss*****ss.****.**dd";
     * 拆分后内容： com.xiyuan.ss**ss， * ， **dd
     *
     * @param str 原始字符串
     * @param spe 拆分字符
     * @return 拆分后字符串
     */
    public static List<String> splitpt(String str, final char spe) {
        List<String> ls = new ArrayList<String>();
        StringBuilder sb = new StringBuilder();
        char p = 0;
        char s = 0;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (spe == c) {
                if (sb.length() > 0) {
                    ls.add(sb.toString());
                    sb.setLength(0);
                }
                p = 0;
                s = 0;
                continue;
            }
            else if ('*' == c) {
                if (++s <= 2 && !(p == '*' && sb.charAt(0) != '*')) {
                    sb.append(c);
                }
            }
            else {
                sb.append(c);
                s = 0;
            }
            
            p = c;
        }
        
        if (sb.length() > 0) {
            ls.add(sb.toString());
            sb.setLength(0);
        }
        
        return ls;
    }
    
    public static String toLowerCase(String s, Locale locale) {
        return s == null ? s : s.toLowerCase(locale);
    }
    
    public static String toLowerCase(String s) {
        return s == null ? s : s.toLowerCase();
    }
    
    public static String toUpperCase(String s, Locale locale) {
        return s == null ? s : s.toUpperCase(locale);
    }
    
    public static String toUpperCase(String s) {
        return s == null ? s : s.toUpperCase();
    }
    
    public static String toCamelCase(String s) {
        return toCamelCase(s, '_');
    }
    
    /**
     * 驼峰式命名法
     * 例如：user_name->userName
     */
    public static String toCamelCase(String s, char separator) {
        if (s == null) {
            return null;
        }
        
        if (s.indexOf(separator) == -1) {
            return s;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == separator) {
                upperCase = true;
                continue;
            }
            
            if (upperCase) {
                upperCase = false;
                c = Character.toUpperCase(c);
            }
            
            sb.append(c);
        }
        return sb.toString();
    }
    
    
    public static char[] toCharArray(String s) {
        return s.toCharArray();
    }
    
    public static boolean equalsIgnoreCase(String s, String a) {
        return (s == null) ? (a == null) : s.equalsIgnoreCase(a);
    }
    
    public static String intern(String s) {
        return s.intern();
    }
    
    /********************************************/
    // 以下为String的静态方法
    /********************************************/
    
    /**
     * 格式化，有%s, %d, %f等
     *
     * @param format 格式，比如 "您已成功注册到%s，用户名：%s，密码：%s，请登录..."
     * @param args   参数列表，比如 {"系统","张三","111111"};
     * @return 格式化后的内容，例子的内容则为： "您已成功注册到系统，用户名：张三，密码：111111，请登录..."
     */
    public static String format(String format, Object... args) {
        return String.format(format, args);
    }
    
    public static String format(Locale l, String format, Object... args) {
        return String.format(l, format, args);
    }
    
    /************************************************************************/
    // 字符串格式化
    /************************************************************************/
    
    /**
     * 格式化占位符，和format方法不同的是：只处理指定的占位符
     *
     * @param pattern     模式， 比如 "您已成功注册到%s，用户名：%s，密码：%s，请登录..."
     * @param placeholder 占位符号 比如 "%s"
     * @param arguments   替换列表，比如 {"系统","张三","111111"};
     * @return 格式化后的内容，例子的内容则为： "您已成功注册到系统，用户名：张三，密码：111111，请登录..."
     */
    public static String formatPlaceholder(String pattern, String placeholder, Object... arguments) {
        if (pattern == null || arguments == null || arguments.length == 0) {
            return pattern;
        }
        
        String[] segmentArr = pattern.split(placeholder);
        if (segmentArr.length <= 1) {
            return pattern;
        }
        
        int len = segmentArr.length;
        StringBuilder strb = new StringBuilder();
        
        int i = 0;
        for (; (i < len - 1 && i < arguments.length); i++) {
            strb.append(segmentArr[i]).append(arguments[i]);
        }
        
        // 后面的加上
        for (int j = i; j < len; j++) {
            strb.append(segmentArr[j]);
        }
        
        return strb.toString();
    }
    
    /**
     * 格式化，支持{}转化为%s，再作format处理，（即：注意如果pattern中有%s,%d等也会生效）
     *
     * @param pattern   模式，比如 "您已成功注册到{}，用户名：{}，密码：{}，请登录..."
     * @param arguments 参数列表，比如 ["系统","张三","111111"];
     * @return 格式化后的内容，例子的内容则为： "您已成功注册到系统，用户名：张三，密码：111111，请登录..."
     */
    public static String formats(String pattern, Object... arguments) {
        if (Validates.isBlank(pattern) || arguments == null || arguments.length == 0) {
            return pattern;
        }
        
        if (pattern.indexOf("{}") != -1) {
            pattern = pattern.replaceAll("\\{\\}", "%s");
        }
        
        return String.format(pattern, arguments);
    }
    
    /**
     * 格式化，采用MessageFormat.format方法按{0},{0,number,#.#}格式进行格式化得到最后数据
     *
     * @param pattern   模式 比如 "您已成功注册到{0}，用户名：{1}，密码：{2}，请登录..."
     * @param arguments 参数表，比如 ["系统","张三","111111"];
     * @return 替换后的内容，例子的内容则为： "您已成功注册到系统，用户名：张三，密码：111111，请登录..."
     */
    public static String formatMessage(String pattern, Object... arguments) {
        return MessageFormat.format(pattern, arguments);
    }
    
    /**
     * 格式化，支持两种格式化，先作formats,再作formatMessage
     *
     * @param pattern   模式，比如 "您已成功注册到{0}，用户名：{1}，密码：{2}，请登录..."
     * @param arguments 参数列表，比如 ["系统","张三","111111"];
     * @return 格式化后的内容，例子的内容则为： "您已成功注册到系统，用户名：张三，密码：111111，请登录..."
     */
    public static String formatTwo(String pattern, Object... arguments) {
        if (Validates.isBlank(pattern) || arguments == null || arguments.length == 0) {
            return pattern;
        }
        
        if (pattern.indexOf("{}") != -1) {
            pattern = pattern.replaceAll("\\{\\}", "%s");
        }
        
        // 1.先作消息文本方案，对处理失败的认为不适用
        try {
            pattern = MessageFormat.format(pattern, arguments);
        }
        catch (IllegalArgumentException e) {
        }
        
        // 2.再作传统字符方案
        return String.format(pattern, arguments);
    }
    
    /**
     * 格式化空格，对多个空格转化为一个空格
     *
     * @param s 要格式化的字符串
     * @return 多空格转为一个空格
     */
    public static String formatSpace(String s) {
        s = trim(s);
        
        StringBuilder strb = new StringBuilder();
        char lastChar = 0;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == _SPACE_ && lastChar == _SPACE_) {
                continue;
            }
            
            strb.append(c);
            lastChar = c;
        }
        
        return strb.toString();
    }
    
    /**
     * 格式化消息和异常，得到消息
     *
     * @param message 消息
     * @param e       异常
     * @return 消息
     */
    public static String formatMessage(String message, Throwable e) {
        if (message != null) {
            return message;
        }
        
        if (e == null) {
            return null;
        }
        
        if (e.getMessage() != null) {
            return e.getMessage();
        }
        
        if (e.getCause() == null) {
            return e.getClass().getName();
        }
        
        if (e.getCause().getMessage() == null) {
            return e.getCause().getClass().getName();
        }
        else {
            return e.getCause().getMessage();
        }
    }
    
    /********************************************************/
    // 以下创建字符串的和获取字符串字节数组方法
    /********************************************************/
    public static String toStringOrNull(Object obj) {
        return null == obj ? null : obj.toString();
    }
    /**
     * 转换为字符串，=NULL时为空字符串
     */
    public static String toString(Object o) {
        return (o == null) ? _EMPTY_ : String.valueOf(o);
    }
    
    /**
     * 转换为字符串，=NULL时为缺省值
     */
    public static String toString(Object o, String dv) {
        return (o == null) ? dv : String.valueOf(o);
    }
    
    
    /**
     * 转换为布尔型，支持[true|false|yes|no|1|0|on|off]
     *
     * @param s 字符串
     * @return true|false
     */
    public static boolean toBoolean(String s) {
        int hash = s == null ? -1 : s.hashCode();
        switch (hash) {
            case 49: // "1".hashCode();
            case 3569038:// "true".hashCode();
            case 2583950:// "TRUE".hashCode();
            case 119527:// "yes".hashCode();
            case 87751:// "YES".hashCode();
            case 3551:// "on".hashCode();
            case 2527:// "ON".hashCode();
                return true;
            default:
                return false;
        }
    }
    
    /**
     * 把字符串，转化成可能的8种基本类型对象和字符串对象，转化失败其不是基本类型返回null
     *
     * @param clazz 转化的类型
     * @param s     字符串
     * @return 如果转化成功，返回该类型的值，否则返回null;
     */
    public static Object toSimpleType(Class<?> clazz, String s) {
        // if (s == null) {return null;}
        
        if (clazz == String.class) {return s;}
        s = s.trim();
        
        int hash = Types.getClassHashCode(clazz);
        switch (hash) {
            case BOOLEAN_CODE:
                return toBoolean(s);
            case BOOLEAN_CODE_OBJ:
                return toBoolean(s) ? Boolean.TRUE : Boolean.FALSE;
            case BYTE_CODE:
                return s == null ? 0 : Byte.parseByte(s);
            case BYTE_CODE_OBJ:
                return s == null ? null : Byte.valueOf(s);
            case CHAR_CODE:
                return s == null ? 0 : s.charAt(0);
            case CHAR_CODE_OBJ:
                return s == null ? null : (Character) s.charAt(0);
            case SHORT_CODE:
                return s == null ? 0 : Short.parseShort(s);
            case SHORT_CODE_OBJ:
                return s == null ? null : Short.valueOf(s);
            case INT_CODE:
                return s == null ? 0 : Integer.parseInt(s);
            case INT_CODE_OBJ:
                return s == null ? null : Integer.valueOf(s);
            case LONG_CODE:
                return s == null ? 0 : Long.parseLong(s);
            case LONG_CODE_OBJ:
                return s == null ? null : Long.valueOf(s);
            case FLOAT_CODE:
                return s == null ? 0 : Float.parseFloat(s);
            case FLOAT_CODE_OBJ:
                return s == null ? null : Float.valueOf(s);
            case DOUBLE_CODE:
                return s == null ? 0 : Double.parseDouble(s);
            case DOUBLE_CODE_OBJ:
                return s == null ? null : Double.valueOf(s);
            case BIG_INTEGER:
                return s == null ? null : new BigInteger(s);
            case BIG_DECIMAL:
                return s == null ? null : new BigDecimal(s);
            case ATOMIC_INTEGER:
                return s == null ? null : new AtomicInteger(Integer.parseInt(s));
            case ATOMIC_LONG:
                return s == null ? null : new AtomicLong(Long.parseLong(s));
            case ATOMIC_BOOLEAN:
                return s == null ? null : new AtomicBoolean(toBoolean(s));
            
            default:
                // 不支持类型
                return null;
        }
        
        
    }
    
    /**
     * 字符串(unicode)转为UTF-8的字节数组，各语言通用
     *
     * @param str 字符串unicode双字节
     * @return 字节数组，单、双、三、四 字节转换
     */
    public static byte[] toUTF8(String str) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        for (int i = 0; i < str.length(); i++) {
            int c = (int) str.charAt(i);
            if (c <= 0x7F) {
                // 0-127 单字节 7 bits
                out.write(c);
            }
            else if (c <= 0x7FF) {
                // 128-2047 双字节 11 bits
                out.write(0xC0 | ((c >> 6) & 0x1F));
                out.write(0x80 | (c & 0x3F));
            }
            else if (c <= 0xFFFF) {
                
                out.write(0xE0 | ((c >> 12) & 0x0F));
                out.write(0x80 | ((c >> 6) & 0x3F));
                out.write(0x80 | (c & 0x3F));
            }
            else {
                // 65535- 四字节 21 bits
                out.write(0xF0 | (c >> 18));
                out.write(0x80 | ((c >> 12) & 0x3F));
                out.write(0x80 | ((c >> 6) & 0x3F));
                out.write(0x80 | (c & 0x3F));
            }
        }
        
        return out.toByteArray();
    }
    
    /**
     * 创建StringBuilder对象
     *
     * @return StringBuilder对象
     */
    public static StringBuilder builder() {
        return new StringBuilder();
    }
    /********************************************************/
    // 以下字符串扩展使用方法
    /********************************************************/
    
    /**
     * 首字母大写
     */
    public static String toUpperFirstChar(String str) {
        if (str == null) {return null;}
        str = trim(str);
        if (str.isEmpty()) {return _EMPTY_;}
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }
    
    /**
     * 验证startsWith, 有一项满足条件即返回true
     *
     * @param s       被验证字符串
     * @param prefixs 验证字符串数组
     * @return true|false
     */
    public static boolean startsWith(String s, String... prefixs) {
        if (prefixs == null) {return s == null;}
        
        if (s == null) {return prefixs == null;}
        
        for (String prefix : prefixs) {
            if (startsWith(s, prefix)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 验证endsWith 有一项满足条件即返回true
     *
     * @param s       被验证字符串
     * @param suffixs 验证字符串数组
     * @return true|false
     */
    public static boolean endsWith(String s, String... suffixs) {
        if (suffixs == null) {
            return s == null;
        }
        
        if (s == null) {
            return suffixs == null;
        }
        
        for (String suffix : suffixs) {
            if (endsWith(s, suffix)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 忽略大小写验证startsWith
     *
     * @param s      被验证字符串
     * @param prefix 验证字符串
     * @return boolean =true表示startsWith, 否则=false
     */
    public static boolean startsWithIgnoreCase(String s, String prefix) {
        return startsWith(s,prefix, true);
    }
    
    /**
     * 忽略大小写验证endsWith
     *
     * @param s      被验证字符串
     * @param suffix 验证字符串
     * @return boolean =true表示endsWith, 否则=false
     */
    public static boolean endsWithIgnoreCase(String s, String suffix) {
        return endsWith(s, suffix,true);
    }
    
    /********************************************************/
    // 以下获取字符串的前缀补位方法
    /********************************************************/
    
    /**
     * 后置补齐，通过给定初始str,和前置char,得到给定长度的值, 通常用于前补0等
     *
     * @param value  原值
     * @param len    给定长度
     * @param suffix 后置char
     * @return String 得到前补字符的字符串
     */
    public static String suffixLen(String value, int len, char suffix) {
        StringBuilder strb = new StringBuilder(value);
        for (int i = 0; i < len; i++) {
            strb.append(suffix);
        }
        
        return strb.substring(0, len);
    }
    
    /**
     * 后置补齐，通过给定初始int,和前置char,得到给定长度的值, 通常用于前补0等
     *
     * @param value  原值
     * @param len    给定长度
     * @param suffix 后置char
     * @return String 得到前补字符的字符串
     */
    public static String suffixLen(int value, int len, char suffix) {
        StringBuilder strb = new StringBuilder(20).append(value);
        for (int i = 0; i < len; i++) {
            strb.append(suffix);
        }
        
        return strb.substring(0, len);
    }
    
    /**
     * 后置补齐，通过给定初始long,和前置char,得到给定长度的值, 通常用于前补0等
     *
     * @param value  原值
     * @param len    给定长度
     * @param suffix 后置char
     * @return String 得到前补字符的字符串
     */
    public static String suffixLen(long value, int len, char suffix) {
        StringBuilder strb = new StringBuilder(38).append(value);
        for (int i = 0; i < len; i++) {
            strb.append(suffix);
        }
        
        return strb.substring(0, len);
    }
    
    /**
     * 获取字符串的整型数字后缀，如ERR:012等取后十进制数字12
     *
     * @param str 字符串
     * @return 数字
     */
    public static int suffixInt(String str) {
        StringBuilder strb = new StringBuilder();
        for (int i = str.length() - 1; i >= 0; i--) {
            char c = str.charAt(i);
            if (c < '0' || c > '9') {
                break;
            }
            
            strb.append(c);
        }
        
        if (strb.length() == 0) {
            return 0;
        }
        
        return Ints.toInt(strb.toString());
    }
    
    /**
     * 前缀补零，得到给定长度的值,补足0
     *
     * @param str 初始str
     * @param len 给定长度
     * @return String
     */
    public static String prefixZero(String str, int len) {
        return prefixLen(str, len, '0');
    }
    
    /**
     * 前缀补齐，通过给定初始str,和前置char,得到给定长度的值, 通常用于前补0等
     *
     * @param value  原值
     * @param len    给定长度
     * @param prefix 前置char
     * @return String 得到前补字符的字符串
     */
    public static String prefixLen(String value, int len, char prefix) {
        int vlen = value.length();
        if (vlen == len) {// 相等原值返回
            return value;
        }
        else if (vlen > len) {// 超长截取
            return value.substring(vlen - len);
        }
        
        // 不够补足
        StringBuilder strb = new StringBuilder(len);
        for (int i = vlen; i < len; i++) {
            strb.append(prefix);
        }
        
        return strb.append(value).toString();
    }
    
    /**
     * 前缀补一个零，用于月份日期前补零，如3月份补成03
     *
     * @param value 原值
     * @param lte   小于等于该值则补零
     * @return 补零后字符
     */
    public static String prefixZeroOne(int value, int lte) {
        return (value <= lte) ? "0" + value : Integer.toString(value);
    }
    
    /**
     * 前缀补齐，得到给定长度的值,补足0
     *
     * @param value 原值
     * @param len   给定长度
     * @return String 得到前补0的字符串
     */
    public static String prefixZero(int value, int len) {
        return fixedLen(value, len, '0', true);
    }
    
    /**
     * 前缀补齐，得到给定长度的值,补足0
     *
     * @param value 初值
     * @param len   给定长度
     * @return String 得到前补字符的字符串
     */
    public static String prefixZero(long value, int len) {
        return fixedLen(value, len, '0', true);
    }
    
    
    /**
     * 前缀补齐，通过给定初始int,和前置char,得到给定长度的值, 通常用于前补0等
     *
     * @param value     初值
     * @param len       给定长度
     * @param fixedchar 前置char
     * @param isLeft    是否前置
     * @return String 得到前补字符的字符串
     */
    public static String fixedLen(long value, int len, char fixedchar, boolean isLeft) {
        int vlen = Longs.length(value);
        if (vlen == len) {return Long.toString(value);}
        StringBuilder strb = new StringBuilder();
        if (vlen < len) {
            // 增加差的长度
            for (int i = vlen; i < len; i++) {strb.append(fixedchar);}
        }
        
        return (isLeft ? strb.append(value) : strb.insert(0, value)).substring(strb.length() - len);
    }
    
    /**
     * 获取字符串的整型数字前缀，如12px,122 str等取前数字
     *
     * @param str 字符串
     * @return 数字
     */
    public static int prefixInt(String str) {
        boolean isNon = '-' == str.charAt(0);
        if (isNon) {
            str = str.substring(1);
        }
        
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c < '0' || c > '9') {
                break;
            }
            
            strb.append(c);
        }
        
        if (strb.length() == 0) {
            return 0;
        }
        
        int num = Ints.toInt(strb.toString());
        return isNon ? -num : num;
    }
    
    /**
     * 获取字符串的浮点型数字前缀，如12.3px,122 str等取前数字
     *
     * @param str 字符串
     * @return 数字
     */
    public static float prefixFloat(String str) {
        boolean isNon = '-' == str.charAt(0);
        if (isNon) {
            str = str.substring(1);
        }
        
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if ((c < '0' || c > '9') && c != '.') {
                break;
            }
            
            strb.append(c);
        }
        
        if (strb.length() == 0) {
            return 0;
        }
        
        // 排除第二个点后面的数字
        String prefix = strb.toString();
        int ind = prefix.indexOf(".");
        if (ind != -1) {
            int ind2 = prefix.indexOf(".", ind + 1);
            if (ind2 != -1) {
                prefix = prefix.substring(0, ind2);
            }
        }
        
        // 前面的可能的0，parseFloat会忽略
        float num = Float.parseFloat(prefix);
        return isNon ? -num : num;
    }
    
    /********************************************************/
    // 以下获取字符串的出现的次数方法
    /********************************************************/
    
    /**
     * 获取一个字符在字符串出现的次数
     */
    public static int getTimes(String src, char c) {
        int times = 0;
        for (int i = 0; i < src.length(); i++) {
            if (c == src.charAt(i)) {
                times++;
            }
        }
        
        return times;
    }
    
    /**
     * 获取一个字符在字符串出现的次数，忽略最前和最后两个
     */
    public static int getTimesIgnoreFirstLast(String src, char c) {
        int times = 0;
        for (int i = 1; i < src.length() - 1; i++) {
            if (c == src.charAt(i)) {
                times++;
            }
        }
        
        return times;
    }
    
    /**
     * 获取一个字符串在另字符串出现的次数
     */
    public static int getTimes(String src, String str) {
        int times = 0;
        if (src == null || str == null) {
            return times;
        }
        
        int start = 0;
        while ((start = src.indexOf(str, start)) != -1) {
            times++;
            start += str.length();
        }
        return times;
    }
    
    /**
     * 判断起始分行符数，Windows(\r\n为2)，Mac(\r)或Linux(\n)为1，0表示没有\r或\n开头
     *
     * @param content 内容
     * @return 分行符数目
     */
    public static int getStartsWithBrNum(String content) {
        if (content.startsWith(_BR_)) {
            return 2;
        }
        else if (content.startsWith(_LR_) || content.startsWith(_LN_)) {
            return 1;
        }
        else {
            return 0;
        }
    }
    
    /**
     * 是否包含乱码数
     */
    public static int getMessyCodeNum(String s) {
        if (isEmpty(s)) {
            return 0;
        }
        
        int num = 0;
        for (int i = 0; i < s.length(); i++) {
            if ((int) s.charAt(i) == 0xFFFD) {
                num++;
            }
        }
        
        return num;
    }
    
    /********************************************************/
    // 以下整理字符串的方法
    
    /********************************************************/
    /**
     * 按照断言，除去字符串头尾部的断言为真的字符，如果字符串是{@code null}，依然返回{@code null}。
     *
     * @param str       要处理的字符串
     * @param mode      {@code -1}表示trimStart，{@code 0}表示trim全部， {@code 1}表示trimEnd
     * @param predicate 断言是否过掉字符，返回{@code true}表述过滤掉，{@code false}表示不过滤
     * @return 除去指定字符后的的字符串，如果原字串为{@code null}，则返回{@code null}
     */
    public static String trim(CharSequence str, int mode, Predicate<Character> predicate) {
        String result;
        if (str == null) {
            result = null;
        } else {
            int length = str.length();
            int start = 0;
            int end = length;// 扫描字符串头部
            if (mode <= 0) {
                while ((start < end) && (predicate.test(str.charAt(start)))) {
                    start++;
                }
            }// 扫描字符串尾部
            if (mode >= 0) {
                while ((start < end) && (predicate.test(str.charAt(end - 1)))) {
                    end--;
                }
            }
            if ((start > 0) || (end < length)) {
                result = str.toString().substring(start, end);
            } else {
                result = str.toString();
            }
        }
        
        return result;
    }
    
    public static String trim(String str) {
        if (str == null) {
            return null;
        }
        
        int len = str.length() - 1, start = 0, end = len;
        while (end > start && Validates.isWhitespace(str.charAt(end))) {end--;}
        while (start <= end && Validates.isWhitespace(str.charAt(start))) {start++;}
        return end == len && start == 0 ? str : str.substring(start, end + 1);
    }
    
    /**
     * 去除前后空白，如果字符串=null，则返回指定的缺省值，支持去除中文空格12288和BOM65279
     */
    public static String trim(String s, String defaultValue) {
        return (s == null) ? defaultValue : trim(s);
    }
    
    
    /**
     * 删除前后缀字符串，如果存在的话
     *
     * @param s      被删除的字符串
     * @param prefix 前缀字符串
     * @param suffix 后缀字符串
     * @return 删除后的字符串
     */
    public static String trim(String s, String prefix, String suffix) {
        if (Validates.isEmpty(s)) {
            return s;
        }
        
        int s1 = 0, s2 = s.length();
        boolean flag = false;
        if (!Validates.isEmpty(prefix) && s.startsWith(prefix)) {
            s1 = prefix.length();
            flag = true;
        }
        
        if (!Validates.isEmpty(suffix) && s.endsWith(suffix)) {
            s2 -= suffix.length();
            flag = true;
        }
        
        return !flag ? s : s.substring(s1, s2);
    }
    
    /**
     * 去除前后空白, 如果去除空白后，为空字符串 则返回null
     */
    public static String trimToNull(String s) {
        String val = trim(s);
        return val == null || val.isEmpty() ? null : val;
    }
    
    /**
     * 去除前后空白,如果为Null 返回 空字符串
     */
    public static String trimToEmpty(String s) {
        return trim(s, "");
    }
    
    /**
     * 删除字符串前面的空白,直到出现内容，支持去除中文空格12288和BOM65279
     */
    public static String trimLeft(String s) {
        if (Validates.isEmpty(s)) {
            return s;
        }
        
        int len = s.length(), i = 0;
        while ((i < len) && Validates.isWhitespace(s.charAt(i))) {i++;}
        return (i > 0) ? s.substring(i) : s;
    }
    
    /**
     * 删除前缀字符串
     *
     * @param s      被删除的字符串
     * @param prefix 前缀字符串
     * @return 删除后的字符串
     */
    public static String trimLeft(String s, String prefix) {
        return trimLeft(s, prefix, false);
    }
    
    /**
     * 删除前缀字符串
     *
     * @param s      被删除的字符串
     * @param prefix 前缀字符串
     * @return 删除后的字符串
     */
    public static String trimLeft(String s, String prefix, boolean ignoreCase) {
        if (Validates.isEmpty(s) || Validates.isEmpty(prefix)) {
            return s;
        }
        
        if (ignoreCase && startsWithIgnoreCase(s, prefix)) {
            return s.substring(prefix.length());
        }
        else {
            return startsWith(s, prefix) ? s.substring(prefix.length()) : s;
        }
    }
    
    /**
     * 删除字符串后面的空白,直到出现内容，支持去除中文空格12288和BOM65279
     */
    public static String trimRight(String s) {
        if (Validates.isEmpty(s)) {
            return s;
        }
        
        int len = s.length(), i = len - 1;
        while ((i > 0) && Validates.isWhitespace(s.charAt(i))) {i--;}
        return (i < len - 1) ? s.substring(0, i + 1) : s;
    }
    
    /**
     * 删除最后的后缀字符串
     *
     * @param s      被删除的字符串
     * @param suffix 后缀字符串
     * @return 删除后的字符串
     */
    public static String trimRight(String s, String suffix) {
        return trimRight(s, suffix, false);
    }
    
    public static String trimRight(String s, String suffix, boolean ignoreCase) {
        if (Validates.isEmpty(s) || Validates.isEmpty(suffix)) {
            return s;
        }
        if (ignoreCase && endsWithIgnoreCase(s, suffix)) {
            return s.substring(0, s.length() - suffix.length());
        }
        else {
            return endsWith(s, suffix) ? s.substring(0, s.length() - suffix.length()) : s;
        }
    }
    
    
    /**
     * 删除最后的后缀字符串
     *
     * @param strb   被删除的字符串
     * @param suffix 后缀字符串
     */
    public static void trimRight(StringBuilder strb, String suffix) {
        if (strb == null || Validates.isEmpty(suffix) || strb.length() < suffix.length()) {
            return;
        }
        
        String end = strb.substring(strb.length() - suffix.length());
        if (suffix.equals(end)) {
            strb.setLength(strb.length() - suffix.length());
        }
    }
    
    /**
     * 删除s中最后可能的多个c字符，如/home/xxxx////， 删除‘/’得到结果为/home/xxxx
     *
     * @param s 被删除的字符串
     * @param c 字符
     * @return 删除之后的字符串
     */
    public static String trimRight(String s, char c) {
        if (Validates.isEmpty(s)) {
            return s;
        }
        
        int len = s.length(), i = len - 1;
        while (i > 0 && s.charAt(i) == c) {i--;}
        
        return (i < len - 1) ? s.substring(0, i + 1) : s;
    }
    
    
    /**
     * 删除字符串前面的一个BR，可能是\r,\n或\r\n
     */
    public static String trimLeftOneBR(String s) {
        if (Validates.isEmpty(s)) {
            return s;
        }
        
        char c = s.charAt(0);
        if (c != _LF_ && c != _CR_) {
            return s;
        }
        
        if (c == _LF_) {
            return s.substring(1);
        }
        
        // 第一个字符是\r的，检查第二个是不是\n
        if (s.length() > 1 && s.charAt(1) == _LF_) {
            return s.substring(2);
        }
        else {
            return s.substring(1);
        }
    }
    
    
    public static String join(CharSequence delimiter, CharSequence... elements) {
        if (elements == null) {throw new NullPointerException();}
        StringJoiner joiner = new StringJoiner(delimiter);
        for (int i = 0; i < elements.length; i++) {joiner.add(elements[i]);}
        return joiner.toString();
    }
    
    /**
     * 增加JSON中的转义字符，使用双引号时，单引号不转义，使用单引号时双引号不转义，不使用引号时都转义<br>
     * 注意默认对 ‘/’ 添加转义符。如：http://127.0.0.1, 转移后变成 http:\/\/127.0.0.1
     *
     * @param str       原字符串
     * @param quotation   使用的引号,` ' `引号,` " ` 表示双引号, 0表示未使用
     * @return 对字符中需要转义的字符，增加转义符
     */
    public static String addEscapeChar(String str, char quotation) {
        return addEscapeChar(str, quotation, true);
    }
    
    /**
     * 增加JSON中的转义字符，使用双引号时，单引号不转义，使用单引号时双引号不转义，不使用引号时都转义
     *
     * @param str         原字符串
     * @param quotation   使用的引号,` ' `引号,` " ` 表示双引号, 0表示未使用
     * @param escapeSlash 是否需要将 ‘/’ 转义，如果是，则转移后 结果为 ‘\/’
     * @return 对字符中需要转义的字符，增加转义符
     */
    public static String addEscapeChar(String str, char quotation, boolean escapeSlash) {
        if (str == null) {
            return null;
        }
        
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            switch (c) {
                case '\\':
                    strb.append("\\\\");
                    break;
                case '"':
                    if ('"' == quotation) {
                        strb.append("\\\"");
                    }
                    else {
                        strb.append(c);
                    }
                    break;
                case '\'':
                    if ('\'' == quotation) {
                        strb.append("\\\'");
                    }
                    else {
                        strb.append(c);
                    }
                    break;// 单引号或无引号时单引号要转义，双引号下的单引号无需处理
                case '\b':
                    strb.append("\\b");
                    break;
                case '\f':
                    strb.append("\\f");
                    break;
                case '\r':
                    strb.append("\\r");
                    break;
                case '\n':
                    strb.append("\\n");
                    break;
                case '\t':
                    strb.append("\\t");
                    break;
                case '/':
                    if (escapeSlash) {
                        strb.append("\\");
                    }
                    strb.append("/");// 斜杆不转义
                    break;
                default:// 其他正常转移或者已知字符正常输出
                    strb.append(c);
                    break;
            }
        }
        
        return strb.toString();
    }
    
    /**
     * 去除JSON中的转义字符
     *
     * @param str 原字符串
     * @return 去除成对引号之后的字符串
     */
    public static String removeEscapeChar(String str) {
        return removeEscapeChar(str, false);
    }
    
    /**
     * 去除JSON中的转义字符
     *
     * @param str          原字符串
     * @param isJsonString 是否为Json 字符串，=true则返回str本身
     * @return 去除成对引号之后的字符串
     */
    public static String removeEscapeChar(String str, boolean isJsonString) {
        if (isJsonString) {return str;}
        
        if (str == null) {return null;}
        
        StringBuilder strb = new StringBuilder(str.length());
        boolean isEscape = false;// 是否前一字符是转义字符
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (!isEscape) {// 未转义
                if (c == '\\') {
                    // 设为有转义
                    isEscape = true;
                }
                else {
                    strb.append(c);
                }
            }
            else {// 有转义
                switch (c) {
                    case '\\':
                        strb.append('\\');
                        break;
                    case 'b':
                        strb.append('\b');
                        break;
                    case 'f':
                        strb.append('\f');
                        break;
                    case 'n':
                        strb.append('\n');
                        break;
                    case 'r':
                        strb.append('\r');
                        break;
                    case 't':
                        strb.append('\t');
                        break;
                    case '/':
                    case '\'':
                    case '\"': {
                        strb.append(c);
                        break;
                    }
                    default:
                        strb.append("\\").append(c);
                        break;// 如果未找到匹配,则返原值
                }
                isEscape = false;// 重置转义为结束
            }
        }
        
        // 最后一个字符是\ 则需要还原
        if (isEscape) {strb.append("\\");}
        return strb.toString();
    }
    
    /***
     * 去除JSON键和值的前后成对引号 注意：字符串首尾不能 有空格
     *
     * @param str 原字符串
     * @return 去除成对引号之后的字符串
     */
    public static String removeStartEndQuotation(String str) {
        if (str == null) {return null;}
        
        int len = str.length();
        if (len < 2) {return str;}
        
        int end = len - 1;
        char c1 = str.charAt(0), c2 = str.charAt(end);
        
        // 有双引号或者单引号删除退出
        if (c1 == c2 && ('"' == c1 || '\'' == c1)) {return str.substring(1, end);}
        
        return str;
    }
    
    // 解析 unicode
    public static String parseUnicode(String value) {
        if (value == null) {return value;}
        
        // 处理unicode 编码的字符
        int fromIndex = value.indexOf("\\u");
        if (fromIndex == -1) {return value;}
        
        StringBuilder strb = new StringBuilder(value);
        do {
            int endIndex = fromIndex + 6;// “\uFFFF” 6个字符长度
            if (endIndex > strb.length()) {
                break;// 字符长度不足以构建成 unicode 字符
            }
            
            fromIndex += 2;// 下一个索引开始
            
            // 前面有转义符则不视为Unicode码
            if (fromIndex > 2 && strb.charAt(fromIndex - 3) == '\\') {continue;}
            
            boolean isUnicode = true;
            for (int i = fromIndex; i < endIndex; i++) {
                char tc = strb.charAt(i);
                // 0123456789ABCDEFabcdef
                if (tc < 48 || (tc > 57 && tc < 65) || (tc > 70 && tc < 97) || tc > 102) {// 0 9 A F a f
                    isUnicode = false;
                    break;
                }
            }
            
            if (isUnicode) {
                char c = (char) Integer.parseInt(strb.substring(fromIndex, endIndex), 16);
                strb.setCharAt(fromIndex - 2, c);
                strb.delete(fromIndex - 1, endIndex);
                fromIndex--;
            }
        }
        while ((fromIndex = strb.indexOf("\\u", fromIndex)) != -1);
        
        return strb.toString();
    }
    /********************************************************/
    // 以下删除字符中指定的字符方法
    /********************************************************/
    
    /**
     * 删除s中出现的所有背包中定义的字符
     * <p>
     * 举例: s = "adddsg"; bag = "ds"; 得到结果是:returnString = "ag";
     *
     * @param s   原字符串
     * @param bag 包字符串
     * @return 删除s中出现的所有bag定义的字符后的字符串
     */
    public static String removeBag(String s, String bag) {
        if (Validates.isEmpty(s) || Validates.isEmpty(bag)) {
            return s;
        }
        
        // 逐个字符检查,如果该字符不在bag中,则加入
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (bag.indexOf(c) > -1) {
                continue;
            }
            
            strb.append(c);
        }
        
        return strb.toString();
    }
    
    /**
     * 删除s中所有背包中未定义的字符
     * <p>
     * 举例: s = "adddsg"; bag = "ds"; 得到结果是:returnString = "ddds";
     *
     * @param s   原字符串
     * @param bag 包字符串
     * @return 删除s中出现的所有bag未定义的字符后的字符串
     */
    public static String removeBagNot(String s, String bag) {
        if (Validates.isEmpty(s)) {
            return s;
        }
        
        if (Validates.isEmpty(bag)) {
            return _EMPTY_;
        }
        
        // 逐个字符检查,如果该字符在bag中,则加入
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (bag.indexOf(c) == -1) {
                continue;
            }
            
            strb.append(c);
        }
        
        return strb.toString();
    }
    
    /**
     * 删除s中所有空白 (包括空格以下(0x00-0x20)之间的所有字符,包括\t,\r,\n,\b等)
     * <p>
     * 举例: s = " a\tb\rc d ";得到结果是:returnString = "abcd";
     *
     * @param s 原字符串
     * @return 删除空白之后的字符串
     */
    public static String removeAllBlank(String s) {
        if (Validates.isEmpty(s)) {
            return s;
        }
        
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (Validates.isWhitespace(c)) {
                continue;
            }
            
            strb.append(c);
        }
        
        return strb.toString();
    }
    
    /**
     * 删除可能的第一个分隔符左边的所有字符，如org.xxx.xxxx，删除后得到xxxx.example
     *
     * @param s         字符串
     * @param separator 分隔符
     * @return 删除后的字符串
     */
    public static String removeLeftByFirst(String s, String separator) {
        if (Validates.isBlank(s)) {
            return _EMPTY_;
        }
        
        s = s.trim();
        int pos = s.indexOf(separator);
        if (pos == -1) {
            return s;
        }
        
        return s.substring(pos + separator.length());
    }
    
    /**
     * 删除可能的最后一个分隔符左边的所有字符，如org.xxxx.example，删除后得到example
     *
     * @param s         字符串
     * @param separator 分隔符
     * @return 删除后的字符串
     */
    public static String removeLeftByLast(String s, String separator) {
        if (Validates.isBlank(s)) {
            return _EMPTY_;
        }
        
        s = s.trim();
        int pos = s.lastIndexOf(separator);
        if (pos == -1) {
            return s;
        }
        
        return s.substring(pos + separator.length());
    }
    
    /**
     * 删除可能的第一个分隔符右边的所有字符，如org.xxxx.example，删除后得到org
     *
     * @param s         字符串
     * @param separator 分隔符
     * @return 删除后的字符串
     */
    public static String removeRightByFirst(String s, String separator) {
        if (Validates.isBlank(s)) {
            return _EMPTY_;
        }
        
        s = s.trim();
        int pos = s.indexOf(separator);
        if (pos == -1) {
            return s;
        }
        else if (pos < 1) {
            return _EMPTY_;
        }
        else {
            return s.substring(0, pos);
        }
    }
    
    /**
     * 删除可能的最后一个分隔符右边的所有字符，如org.xxxx.example，删除后得到org.xxxx
     *
     * @param s         字符串
     * @param separator 分隔符
     * @return 删除后的字符串
     */
    public static String removeRightByLast(String s, String separator) {
        if (Validates.isBlank(s)) {
            return _EMPTY_;
        }
        
        s = s.trim();
        int pos = s.lastIndexOf(separator);
        if (pos == -1) {
            return s;
        }
        else if (pos < 1) {
            return _EMPTY_;
        }
        else {
            return s.substring(0, pos);
        }
    }
    
    /**
     * 删除可能的最后一个空白，保留\r\n
     *
     * @param s 字符串
     * @return 删除后的字符串
     */
    public static String removeRightMaybeEmptyBlank(String s) {
        if (Validates.isBlank(s)) {
            return _EMPTY_;
        }
        
        int index = -1;
        for (int i = s.length() - 1; i >= 0; i--) {
            char c = s.charAt(i);
            if (c == _LF_ || c == _CR_) {
                index = i;
                break;
            }
            
            if (!Validates.isWhitespace(c)) {
                break;
            }
        }
        
        return (index == -1) ? s : s.substring(0, index + 1);
    }
    
    /**
     * 删除可能的最后一个空白，保留\r\n
     *
     * @param strb 字符串
     */
    public static void removeRightMaybeEmptyBlank(StringBuilder strb) {
        // 找到回车或换行的位置
        int index = -1;
        boolean allEmptyBlank = true;
        for (int i = strb.length() - 1; i >= 0; i--) {
            char c = strb.charAt(i);
            if (c == _LF_ || c == _CR_) {
                index = i;
                allEmptyBlank = false;
                break;
            }
            
            if (!Validates.isWhitespace(c)) {
                allEmptyBlank = false;
                break;
            }
        }
        
        if (allEmptyBlank) {
            strb.setLength(0);
        }
        else if (index != -1) {
            strb.setLength(index + 1);
        }
    }
    
    /**
     * 删除可能的最后一个空行
     *
     * @param s 字符串
     * @return 删除后的字符串
     */
    public static String removeRightMaybeEmptyBlankLine(String s) {
        if (Validates.isBlank(s)) {
            return _EMPTY_;
        }
        
        int index = -1;
        for (int i = s.length() - 1; i >= 0; i--) {
            char c = s.charAt(i);
            if (c == _LF_) {// 当最后为\n时再检查前面是否有\r，Windows使用\r\n分行
                if (i > 0 && s.charAt(i - 1) == _CR_) {
                    index = i - 1;
                }
                else {
                    index = i;
                }
                break;
            }
            else if (c == _CR_) {// Mac机器使用\r分行
                index = i;
                break;
            }
            else if (!Validates.isWhitespace(c)) {
                break;
            }
        }
        
        return (index == -1) ? s : s.substring(0, index);
    }
    
    /**
     * 删除可能的最后一个空行
     *
     * @param strb 字符串
     */
    public static void removeRightMaybeEmptyBlankLine(StringBuilder strb) {
        int index = -1;
        for (int i = strb.length() - 1; i >= 0; i--) {
            char c = strb.charAt(i);
            if (c == _LF_) {// 当最后为\n时再检查前面是否有\r，Windows使用\r\n分行
                if (i > 0 && strb.charAt(i - 1) == _CR_) {
                    index = i - 1;
                }
                else {
                    index = i;
                }
                break;
            }
            else if (c == _CR_) {// Mac机器使用\r分行
                index = i;
                break;
            }
            
            if (Validates.isWhitespace(c)) {
                break;
            }
        }
        
        if (index != -1) {
            strb.setLength(index);
        }
    }
    
    /**
     * 删除s中前缀字符串，如果有的话，功能同trimLeft
     *
     * @param s          被删除的字符串
     * @param startsWith 前缀字符串
     * @return 删除后的字符串
     */
    public static String removeStartsWith(String s, String startsWith) {
        return trimLeft(s, startsWith);
    }
    
    /**
     * 删除s中后缀字符串，如果有的话，功能同trimRight
     *
     * @param s        被删除的字符串
     * @param endsWith 后缀字符串
     * @return 删除后的字符串
     */
    public static String removeEndsWith(String s, String endsWith) {
        return trimRight(s, endsWith);
    }
    
    /**
     * 删除s中后缀字符串，如果有的话，功能同trimRight
     *
     * @param strb     被删除的字符串
     * @param endsWith 后缀字符串
     */
    public static void removeEndsWith(StringBuilder strb, String endsWith) {
        trimRight(strb, endsWith);
    }
    
    /**
     * 增加s中前缀字符串，如果需要的话
     * <p>
     * 举例 : 1) s == null, startsWith == null，返回null 2) s == null, startsWith == ""，返回"" 3) s != null, startsWith == null
     * || == ""，返回s 4) s.startsWith(startsWith)，返回s 5) !s.startsWith(startsWith)，返回startsWith + s
     *
     * @param s          原字符串
     * @param startsWith 前缀字符串
     * @return 增加后的字符串
     */
    public static String addStartsWith(String s, String startsWith) {
        if (s == null) {return startsWith;}
        if (Validates.isEmpty(startsWith)) {return s;}
        if (s.startsWith(startsWith)) {return s;}
        return startsWith + s;
    }
    
    /**
     * 增加s中后缀字符串，如果需要的话
     * <p>
     * 举例 : 1) s == null, endsWith == null，返回null 2) s == null, endsWith == ""，返回"" 3) s != null, endsWith == null || ==
     * ""，返回s 4) s.endsWith(endsWith)，返回s 5) !s.endsWith(endsWith)，返回s + endsWith
     *
     * @param s        原字符串
     * @param endsWith 前缀字符串
     * @return 增加后的字符串
     */
    public static String addEndsWith(String s, String endsWith) {
        if (s == null) {return endsWith;}
        if (Validates.isEmpty(endsWith)) {return s;}
        if (s.endsWith(endsWith)) {return s;}
        return s + endsWith;
    }
    
    /**
     * 增加新行\r\n
     *
     * @param strb 变长字符串
     */
    public static StringBuilder addNewLine(StringBuilder strb) {
        return strb.append(_BR_);
    }
    
    /**
     * 增加新行\r\n
     *
     * @param strb 变长字符串
     */
    public static StringBuffer addNewLine(StringBuffer strb) {
        return strb.append(_BR_);
    }
    
    /**
     * 增加多个双空格
     *
     * @param strb 变长字符串
     * @param num  双空格数
     */
    public static StringBuilder addTwoSpace(StringBuilder strb, int num) {
        for (int i = 0; i < num; i++) {
            strb.append(_TWO_SPACE_);
        }
        return strb;
    }
    
    /**
     * 指定内容，得到每行的起始和结尾在内容的总索引位置列表<br>
     *
     * @param content 需要指定的内容
     * @return List<ZmlLineIndex> 行索引号列表
     */
    public static List<Line> getIndexList(String content) {
        int lineNo = 0;
        ArrayList<Line> indexList = new ArrayList<Line>();
        StringTokenizer tokener = new StringTokenizer(content, _BR_, true);
        boolean f = false, f2 = false;
        while (tokener.hasMoreTokens()) {// 注意转化为第一行开始
            String linestr = tokener.nextToken();
            if (_LN_.equals(linestr) || _LR_.equals(linestr)) {// 空行不加入
                if (!f) {
                    // 如果换行了则记录行号+1
                    f = true;
                    f2 = true;
                    lineNo++;
                }
                else {
                    f = false;
                }
                
                continue;
            }
            
            if (!f2) {
                lineNo++;
            }
            
            f = false;
            f2 = false;
            
            Line line = new Line();
            line.setLineNo(lineNo);
            line.setContent(linestr);
            indexList.add(line);
        }
        
        indexList.trimToSize();
        return indexList;
    }
    
    /**
     * 增加多个双空格
     *
     * @param strb 变长字符串
     * @param num  双空格数
     */
    public static StringBuffer addTwoSpace(StringBuffer strb, int num) {
        for (int i = 0; i < num; i++) {
            strb.append(_TWO_SPACE_);
        }
        return strb;
    }
    
    /**
     * 增加多个四空格
     *
     * @param strb 变长字符串
     * @param num  四空格数
     */
    public static StringBuilder addFourSpace(StringBuilder strb, int num) {
        for (int i = 0; i < num; i++) {
            strb.append(_FOUR_SPACE_);
        }
        
        return strb;
    }
    
    /**
     * 增加多个四空格
     *
     * @param strb 变长字符串
     * @param num  四空格数
     */
    public static StringBuffer addFourSpace(StringBuffer strb, int num) {
        for (int i = 0; i < num; i++) {
            strb.append(_FOUR_SPACE_);
        }
        return strb;
    }
    
    /**
     * 增加多个制表符
     *
     * @param strb 变长字符串
     * @param num  制表符数
     */
    public static StringBuilder addTab(StringBuilder strb, int num) {
        for (int i = 0; i < num; i++) {
            strb.append(_TAB_);
        }
        
        return strb;
    }
    
    /**
     * 增加多个制表符
     *
     * @param strb 变长字符串
     * @param num  制表符数
     */
    public static StringBuffer addTab(StringBuffer strb, int num) {
        for (int i = 0; i < num; i++) {
            strb.append(_TAB_);
        }
        return strb;
    }
    
    /**
     * 从sources 查找字符串 使用equals算法查找集合项
     *
     * @param target  目标字符串
     * @param sources 集合列表
     * @return true|false
     */
    public static boolean find(String target, CharSequence... sources) {
        if (sources == null) {
            return false;
        }
        
        for (CharSequence chs : sources) {
            if (chs == null && target == null) return true;
            if (chs != null && chs.equals(target)) return true;
        }
        
        return false;
    }
    
    /**
     * Join.
     *
     * @param delim       the delim
     * @param collections the collections
     * @return the string
     */
    public static String join(String delim, Collection<String> collections) {
        String[] values = new String[collections.size()];
        collections.toArray(values);
        return join(delim, 0, collections.size(), values);
    }
    
    /**
     * Join.
     *
     * @param delim   the delim
     * @param strings the strings
     * @return the string
     */
    public static String join(String delim, String... strings) {
        return join(delim, 0, strings.length, strings);
    }
    
    /**
     * Join.
     *
     * @param delim   the delim
     * @param start   the start
     * @param end     the end
     * @param strings the strings
     * @return the string
     */
    public static String join(String delim, int start, int end, String... strings) {
        if (strings.length == 1) {
            return strings[0];
        }
        StringBuilder sb = new StringBuilder(strings[start]);
        for (int i = start + 1; i < end; i++) {
            sb.append(delim).append(strings[i]);
        }
        return sb.toString();
    }
    
    
    /**
     * 左填充
     * <pre>
     *     String str1 = lpad("张三", 5, "*");
     *     // 输出：“***张三”
     *
     *     String str1 = lpad("张三", 5, "123");
     *     // 输出：“123123张三”
     *
     * </pre>
     *
     * @param str     the str
     * @param len     the len
     * @param padding the padding
     * @return the string
     */
    public static String lpad(Object str, int len, String padding) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len - str.toString().length(); i++) {
            sb.append(padding);
        }
        sb.append(str);
        return sb.toString();
    }
    
    /**
     * 右填充
     * <pre>
     *     String str1 = lpad("张三", 5, "*");
     *     // 输出：“张三***”
     *
     *     String str1 = lpad("张三", 5, "123");
     *     // 输出：“张三123”
     *
     * @param str     the str
     * @param len     the len
     * @param padding the padding
     * @return the string
     */
    public static String rpad(Object str, int len, String padding) {
        StringBuilder sb = new StringBuilder();
        sb.append(str);
        for (int i = 0; i < len - str.toString().length(); i++) {
            sb.append(padding);
        }
        return sb.toString();
    }
    
    /**
     * 重复某个字符
     *
     * <pre>
     * StrUtil.repeat('e', 0)  = ""
     * StrUtil.repeat('e', 3)  = "eee"
     * StrUtil.repeat('e', -2) = ""
     * </pre>
     *
     * @param c     被重复的字符
     * @param count 重复的数目，如果小于等于0则返回""
     * @return 重复字符字符串
     */
    public static String repeat(char c, int count) {
        if (count <= 0) return _EMPTY_;
        char[] result = new char[count];
        Arrays.fill(result, c);
        return new String(result);
    }
    
    // ------------------------------------------------------------------------ sub
    
    /**
     * 改进JDK subString<br>
     * index从0开始计算，最后一个字符为-1<br>
     * 如果from和to位置一样，返回 "" <br>
     * 如果from或to为负数，则按照length从后向前数位置，如果绝对值大于字符串长度，则from归到0，to归到length<br>
     * 如果经过修正的index中from大于to，则互换from和to example: <br>
     * abcdefgh 2 3 =》 c <br>
     * abcdefgh 2 -3 =》 cde <br>
     *
     * @param str              String
     * @param fromIndexInclude 开始的index（包括）
     * @param toIndexExclude   结束的index（不包括）
     * @return 字串
     */
    public static String sub(CharSequence str, int fromIndexInclude, int toIndexExclude) {
        if (isEmpty(str)) {
            return str(str);
        }
        int len = str.length();
        
        if (fromIndexInclude < 0) {
            fromIndexInclude = len + fromIndexInclude;
            if (fromIndexInclude < 0) {
                fromIndexInclude = 0;
            }
        } else if (fromIndexInclude > len) {
            fromIndexInclude = len;
        }
        
        if (toIndexExclude < 0) {
            toIndexExclude = len + toIndexExclude;
            if (toIndexExclude < 0) {
                toIndexExclude = len;
            }
        } else if (toIndexExclude > len) {
            toIndexExclude = len;
        }
        
        if (toIndexExclude < fromIndexInclude) {
            int tmp = fromIndexInclude;
            fromIndexInclude = toIndexExclude;
            toIndexExclude = tmp;
        }
        
        if (fromIndexInclude == toIndexExclude) {
            return EMPTY;
        }
        
        return str.toString().substring(fromIndexInclude, toIndexExclude);
    }
    
    /**
     * 通过CodePoint截取字符串，可以截断Emoji
     *
     * @param str       String
     * @param fromIndex 开始的index（包括）
     * @param toIndex   结束的index（不包括）
     * @return 字串
     */
    public static String subByCodePoint(CharSequence str, int fromIndex, int toIndex) {
        if (isEmpty(str)) {
            return str(str);
        }
        
        if (fromIndex < 0 || fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        
        if (fromIndex == toIndex) {
            return EMPTY;
        }
        
        final StringBuilder sb = new StringBuilder();
        final int subLen = toIndex - fromIndex;
        str.toString().codePoints().skip(fromIndex).limit(subLen).forEach(v -> sb.append(Character.toChars(v)));
        return sb.toString();
    }
    
    /**
     * 截取部分字符串，这里一个汉字的长度认为是2
     *
     * @param str    字符串
     * @param len    bytes切割到的位置（包含）
     * @param suffix 切割后加上后缀
     * @return 切割后的字符串
     *
     */
    public static String subPreGbk(CharSequence str, int len, CharSequence suffix) {
        return subPreGbk(str, len, true) + suffix;
    }
    
    /**
     * 截取部分字符串，这里一个汉字的长度认为是2<br>
     * 可以自定义halfUp，如len为10，如果截取后最后一个字符是半个字符，{@code true}表示保留，则长度是11，否则长度9
     *
     * @param str    字符串
     * @param len    bytes切割到的位置（包含）
     * @param halfUp 遇到截取一半的GBK字符，是否保留。
     * @return 切割后的字符串
     *
     */
    public static String subPreGbk(CharSequence str, int len, boolean halfUp) {
        if (isEmpty(str)) {
            return str(str);
        }
        
        int counterOfDoubleByte = 0;
        final byte[] b = str.toString().getBytes(_GBK_C_);
        if (b.length <= len) {
            return str.toString();
        }
        for (int i = 0; i < len; i++) {
            if (b[i] < 0) {
                counterOfDoubleByte++;
            }
        }
        
        if (counterOfDoubleByte % 2 != 0) {
            if (halfUp) {
                len += 1;
            } else {
                len -= 1;
            }
        }
        return new String(b, 0, len, _GBK_C_);
    }
    
    /**
     * 切割指定位置之前部分的字符串
     *
     * @param string         字符串
     * @param toIndexExclude 切割到的位置（不包括）
     * @return 切割后的剩余的前半部分字符串
     */
    public static String subPre(CharSequence string, int toIndexExclude) {
        return sub(string, 0, toIndexExclude);
    }
    
    /**
     * 切割指定位置之后部分的字符串
     *
     * @param string    字符串
     * @param fromIndex 切割开始的位置（包括）
     * @return 切割后后剩余的后半部分字符串
     */
    public static String subSuf(CharSequence string, int fromIndex) {
        if (isEmpty(string)) {
            return null;
        }
        return sub(string, fromIndex, string.length());
    }
    
    /**
     * 切割指定长度的后部分的字符串
     *
     * <pre>
     * StrUtil.subSufByLength("abcde", 3)      =    "cde"
     * StrUtil.subSufByLength("abcde", 0)      =    ""
     * StrUtil.subSufByLength("abcde", -5)     =    ""
     * StrUtil.subSufByLength("abcde", -1)     =    ""
     * StrUtil.subSufByLength("abcde", 5)       =    "abcde"
     * StrUtil.subSufByLength("abcde", 10)     =    "abcde"
     * StrUtil.subSufByLength(null, 3)               =    null
     * </pre>
     *
     * @param string 字符串
     * @param length 切割长度
     * @return 切割后后剩余的后半部分字符串
     *
     */
    public static String subSufByLength(CharSequence string, int length) {
        if (isEmpty(string)) {
            return null;
        }
        if (length <= 0) {
            return EMPTY;
        }
        return sub(string, -length, string.length());
    }
    
    /**
     * 截取字符串,从指定位置开始,截取指定长度的字符串<br>
     * 如果fromIndex为正数，则向后截取指定length长度，如果为负数，则向前截取length长度。
     *
     * @param input     原始字符串
     * @param fromIndex 开始的index,包括
     * @param length    要截取的长度
     * @return 截取后的字符串
     */
    public static String subWithLength(String input, int fromIndex, int length) {
        final int toIndex;
        if(fromIndex < 0){
            toIndex = fromIndex - length;
        }else{
            toIndex = fromIndex + length;
        }
        return sub(input, fromIndex, toIndex);
    }
    
    /**
     * 截取分隔字符串之前的字符串，不包括分隔字符串<br>
     * 如果给定的字符串为空串（null或""）或者分隔字符串为null，返回原字符串<br>
     * 如果分隔字符串为空串""，则返回空串，如果分隔字符串未找到，返回原字符串，举例如下：
     *
     * <pre>
     * StrUtil.subBefore(null, *, false)      = null
     * StrUtil.subBefore("", *, false)        = ""
     * StrUtil.subBefore("abc", "a", false)   = ""
     * StrUtil.subBefore("abcba", "b", false) = "a"
     * StrUtil.subBefore("abc", "c", false)   = "ab"
     * StrUtil.subBefore("abc", "d", false)   = "abc"
     * StrUtil.subBefore("abc", "", false)    = ""
     * StrUtil.subBefore("abc", null, false)  = "abc"
     * </pre>
     *
     * @param string          被查找的字符串
     * @param separator       分隔字符串（不包括）
     * @param isLastSeparator 是否查找最后一个分隔字符串（多次出现分隔字符串时选取最后一个），true为选取最后一个
     * @return 切割后的字符串
     *
     */
    public static String subBefore(CharSequence string, CharSequence separator, boolean isLastSeparator) {
        if (isEmpty(string) || separator == null) {
            return null == string ? null : string.toString();
        }
        
        final String str = string.toString();
        final String sep = separator.toString();
        if (sep.isEmpty()) {
            return EMPTY;
        }
        final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
        if (INDEX_NOT_FOUND == pos) {
            return str;
        }
        if (0 == pos) {
            return EMPTY;
        }
        return str.substring(0, pos);
    }
    
    /**
     * 截取分隔字符串之前的字符串，不包括分隔字符串<br>
     * 如果给定的字符串为空串（null或""）或者分隔字符串为null，返回原字符串<br>
     * 如果分隔字符串未找到，返回原字符串，举例如下：
     *
     * <pre>
     * StrUtil.subBefore(null, *, false)      = null
     * StrUtil.subBefore("", *, false)        = ""
     * StrUtil.subBefore("abc", 'a', false)   = ""
     * StrUtil.subBefore("abcba", 'b', false) = "a"
     * StrUtil.subBefore("abc", 'c', false)   = "ab"
     * StrUtil.subBefore("abc", 'd', false)   = "abc"
     * </pre>
     *
     * @param string          被查找的字符串
     * @param separator       分隔字符串（不包括）
     * @param isLastSeparator 是否查找最后一个分隔字符串（多次出现分隔字符串时选取最后一个），true为选取最后一个
     * @return 切割后的字符串
     *
     */
    public static String subBefore(CharSequence string, char separator, boolean isLastSeparator) {
        if (isEmpty(string)) {
            return null == string ? null : EMPTY;
        }
        
        final String str = string.toString();
        final int pos = isLastSeparator ? str.lastIndexOf(separator) : str.indexOf(separator);
        if (INDEX_NOT_FOUND == pos) {
            return str;
        }
        if (0 == pos) {
            return EMPTY;
        }
        return str.substring(0, pos);
    }
    
    /**
     * 截取分隔字符串之后的字符串，不包括分隔字符串<br>
     * 如果给定的字符串为空串（null或""），返回原字符串<br>
     * 如果分隔字符串为空串（null或""），则返回空串，如果分隔字符串未找到，返回空串，举例如下：
     *
     * <pre>
     * StrUtil.subAfter(null, *, false)      = null
     * StrUtil.subAfter("", *, false)        = ""
     * StrUtil.subAfter(*, null, false)      = ""
     * StrUtil.subAfter("abc", "a", false)   = "bc"
     * StrUtil.subAfter("abcba", "b", false) = "cba"
     * StrUtil.subAfter("abc", "c", false)   = ""
     * StrUtil.subAfter("abc", "d", false)   = ""
     * StrUtil.subAfter("abc", "", false)    = "abc"
     * </pre>
     *
     * @param string          被查找的字符串
     * @param separator       分隔字符串（不包括）
     * @param isLastSeparator 是否查找最后一个分隔字符串（多次出现分隔字符串时选取最后一个），true为选取最后一个
     * @return 切割后的字符串
     *
     */
    public static String subAfter(CharSequence string, CharSequence separator, boolean isLastSeparator) {
        if (isEmpty(string)) {
            return null == string ? null : EMPTY;
        }
        if (separator == null) {
            return EMPTY;
        }
        final String str = string.toString();
        final String sep = separator.toString();
        final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
        if (INDEX_NOT_FOUND == pos || (string.length() - 1) == pos) {
            return EMPTY;
        }
        return str.substring(pos + separator.length());
    }
    
    /**
     * 截取分隔字符串之后的字符串，不包括分隔字符串<br>
     * 如果给定的字符串为空串（null或""），返回原字符串<br>
     * 如果分隔字符串为空串（null或""），则返回空串，如果分隔字符串未找到，返回空串，举例如下：
     *
     * <pre>
     * StrUtil.subAfter(null, *, false)      = null
     * StrUtil.subAfter("", *, false)        = ""
     * StrUtil.subAfter("abc", 'a', false)   = "bc"
     * StrUtil.subAfter("abcba", 'b', false) = "cba"
     * StrUtil.subAfter("abc", 'c', false)   = ""
     * StrUtil.subAfter("abc", 'd', false)   = ""
     * </pre>
     *
     * @param string          被查找的字符串
     * @param separator       分隔字符串（不包括）
     * @param isLastSeparator 是否查找最后一个分隔字符串（多次出现分隔字符串时选取最后一个），true为选取最后一个
     * @return 切割后的字符串
     *
     */
    public static String subAfter(CharSequence string, char separator, boolean isLastSeparator) {
        if (isEmpty(string)) {
            return null == string ? null : EMPTY;
        }
        final String str = string.toString();
        final int pos = isLastSeparator ? str.lastIndexOf(separator) : str.indexOf(separator);
        if (INDEX_NOT_FOUND == pos) {
            return EMPTY;
        }
        return str.substring(pos + 1);
    }
    
    /**
     * 截取指定字符串中间部分，不包括标识字符串<br>
     * <p>
     * 栗子：
     *
     * <pre>
     * StrUtil.subBetween("wx[b]yz", "[", "]") = "b"
     * StrUtil.subBetween(null, *, *)          = null
     * StrUtil.subBetween(*, null, *)          = null
     * StrUtil.subBetween(*, *, null)          = null
     * StrUtil.subBetween("", "", "")          = ""
     * StrUtil.subBetween("", "", "]")         = null
     * StrUtil.subBetween("", "[", "]")        = null
     * StrUtil.subBetween("yabcz", "", "")     = ""
     * StrUtil.subBetween("yabcz", "y", "z")   = "abc"
     * StrUtil.subBetween("yabczyabcz", "y", "z")   = "abc"
     * </pre>
     *
     * @param str    被切割的字符串
     * @param before 截取开始的字符串标识
     * @param after  截取到的字符串标识
     * @return 截取后的字符串
     *
     */
    public static String subBetween(CharSequence str, CharSequence before, CharSequence after) {
        if (str == null || before == null || after == null) {
            return null;
        }
        
        final String str2 = str.toString();
        final String before2 = before.toString();
        final String after2 = after.toString();
        
        final int start = str2.indexOf(before2);
        if (start != INDEX_NOT_FOUND) {
            final int end = str2.indexOf(after2, start + before2.length());
            if (end != INDEX_NOT_FOUND) {
                return str2.substring(start + before2.length(), end);
            }
        }
        return null;
    }
    
    /**
     * 截取指定字符串中间部分，不包括标识字符串<br>
     * <p>
     * 栗子：
     *
     * <pre>
     * StrUtil.subBetween(null, *)            = null
     * StrUtil.subBetween("", "")             = ""
     * StrUtil.subBetween("", "tag")          = null
     * StrUtil.subBetween("tagabctag", null)  = null
     * StrUtil.subBetween("tagabctag", "")    = ""
     * StrUtil.subBetween("tagabctag", "tag") = "abc"
     * </pre>
     *
     * @param str            被切割的字符串
     * @param beforeAndAfter 截取开始和结束的字符串标识
     * @return 截取后的字符串
     *
     */
    public static String subBetween(CharSequence str, CharSequence beforeAndAfter) {
        return subBetween(str, beforeAndAfter, beforeAndAfter);
    }
    
    /**
     * 截取指定字符串多段中间部分，不包括标识字符串<br>
     * <p>
     * 栗子：
     *
     * <pre>
     * StrUtil.subBetweenAll("wx[b]y[z]", "[", "]") 		= ["b","z"]
     * StrUtil.subBetweenAll(null, *, *)          			= []
     * StrUtil.subBetweenAll(*, null, *)          			= []
     * StrUtil.subBetweenAll(*, *, null)          			= []
     * StrUtil.subBetweenAll("", "", "")          			= []
     * StrUtil.subBetweenAll("", "", "]")         			= []
     * StrUtil.subBetweenAll("", "[", "]")        			= []
     * StrUtil.subBetweenAll("yabcz", "", "")     			= []
     * StrUtil.subBetweenAll("yabcz", "y", "z")   			= ["abc"]
     * StrUtil.subBetweenAll("yabczyabcz", "y", "z")   		= ["abc","abc"]
     * StrUtil.subBetweenAll("[yabc[zy]abcz]", "[", "]");   = ["zy"]           重叠时只截取内部，
     * </pre>
     *
     * @param str    被切割的字符串
     * @param prefix 截取开始的字符串标识
     * @param suffix 截取到的字符串标识
     * @return 截取后的字符串

     *
     */
    public static String[] subBetweenAll(CharSequence str, CharSequence prefix, CharSequence suffix) {
        if (hasEmpty(str, prefix, suffix) ||
                // 不包含起始字符串，则肯定没有子串
                !contains(str, prefix)) {
            return new String[0];
        }
        
        final List<String> result = new LinkedList<>();
        final String[] split = split(str.toString(), prefix.toString());
        if (prefix.equals(suffix)) {
            // 前后缀字符相同，单独处理
            for (int i = 1, length = split.length - 1; i < length; i += 2) {
                result.add(split[i]);
            }
        } else {
            int suffixIndex;
            String fragment;
            for (int i = 1; i < split.length; i++) {
                fragment = split[i];
                suffixIndex = fragment.indexOf(suffix.toString());
                if (suffixIndex > 0) {
                    result.add(fragment.substring(0, suffixIndex));
                }
            }
        }
        
        return result.toArray(new String[0]);
    }
    
    /**
     * 截取指定字符串多段中间部分，不包括标识字符串<br>
     * <p>
     * 栗子：
     *
     * <pre>
     * StrUtil.subBetweenAll(null, *)          			= []
     * StrUtil.subBetweenAll(*, null)          			= []
     * StrUtil.subBetweenAll(*, *)          			= []
     * StrUtil.subBetweenAll("", "")          			= []
     * StrUtil.subBetweenAll("", "#")         			= []
     * StrUtil.subBetweenAll("gotanks", "")     		= []
     * StrUtil.subBetweenAll("#gotanks#", "#")   		= ["gotanks"]
     * StrUtil.subBetweenAll("#hello# #world#!", "#")   = ["hello", "world"]
     * StrUtil.subBetweenAll("#hello# world#!", "#");   = ["hello"]
     * </pre>
     *
     * @param str             被切割的字符串
     * @param prefixAndSuffix 截取开始和结束的字符串标识
     * @return 截取后的字符串

     *
     */
    public static String[] subBetweenAll(CharSequence str, CharSequence prefixAndSuffix) {
        return subBetweenAll(str, prefixAndSuffix, prefixAndSuffix);
    }
    
    /**
     * 把字符串，转化成可能的8种基本类型对象和字符串对象，转化失败其不是基本类型返回null
     *
     * @param clazz 转化的类型
     * @param s     字符串
     * @return 如果转化成功，返回该类型的值，否则返回null;
     */
    public static Object toObject(Class<?> clazz, String s)
    {
        if (s == null)
            return null;
        
        if (clazz == Object.class || clazz == String.class)
            return s;
        
        s = s.trim();
        
        try
        {
            if (clazz == byte.class || clazz == Byte.class)
                return Byte.valueOf(s);
            else if (clazz == char.class || clazz == Character.class)
                return new Character(s.charAt(0));
            else if (clazz == int.class || clazz == Integer.class)
                return Integer.valueOf(s);
            else if (clazz == long.class || clazz == Long.class)
                return Long.valueOf(s);
            else if (clazz == short.class || clazz == Short.class)
                return Short.valueOf(s);
            else if (clazz == float.class || clazz == Float.class)
                return Float.valueOf(s);
            else if (clazz == double.class || clazz == Double.class)
                return Double.valueOf(s);
            else if (clazz == boolean.class || clazz == Boolean.class)
                return Boolean.valueOf(s);
            
        }
        catch (Exception e)
        {
            return null;
        }
        
        // 非基本类型和字符串不支持
        return null;
    }
    
    
}
